#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Create by zhang
# Create on 2022-12-07 10:01
import time, datetime

from core import threadLocal
from core.dataClasses import Event, R
from core.enums import DateFormat, Period, EventType, ThreadName
from event.event_manager import EventManager
from infrastructure.util import dateutils
from core import logger


class Clock:
    instances = {
        "default": None
    }

    @classmethod
    def getInstance(cls, name: str = "default"):
        if name is None:
            name = "default"
        if name not in cls.instances or cls.instances[name] is None:
            cls.instances[name] = cls(name)
        try:
            threadLocal.get(ThreadName.CLOCK.value)
        except:
            threadLocal.add(ThreadName.CLOCK.value, name)
        return cls.instances[name]

    def __init__(self, name: str, date:str = None, dateFormat: DateFormat = DateFormat.Y_m_d):
        self._currentDateTime: datetime.datetime = None
        if date is not None:
            self.setCurrentDateTime(date, dateFormat)
        self.event_manager: EventManager = EventManager.getInstance(threadLocal.get(ThreadName.EVENT_MANAGER.value))
        r = R.sucess(f"初始化时钟成功，当前时间：{self.getDateTime().strftime(DateFormat.Y_m_d_H_M_S.value)}", self.getDateTime())
        self.event_manager.emit(Event(EventType.TIP_COMMON, r))
        logger.info(r)

    def getDateTime(self) -> datetime.datetime:
        if self._currentDateTime is None:
            now = datetime.datetime.now()
            tdt = dateutils.getLastestTradingDate(now)
            hm = now.hour * 100 + now.minute
            if tdt.day == now.day and tdt.month == now.month:
                if hm < 930:
                    delta = datetime.timedelta(days=1)
                    dt = now - delta
                    dt = now.replace(hour=15, minute=0, second=0)
                    return dt
                elif 1130 < hm < 1300:
                    return now.replace(hour=11, minute=30, second=0)
                elif hm > 1500:
                    return now.replace(hour=15, minute=0, second=0)
                else:
                    return now
            else:
                tdt = tdt.replace(hour=15, minute=0, second=0)
                return tdt
        else:
            return self._currentDateTime

    def setCurrentDateTime(self, date: str, dateFormat: DateFormat = DateFormat.Y_m_d_H_M_S, period: Period=Period.day):
        """

        :param date:
        :param dateFormat:
        :param lastestFinishedUnit: 是否设置当前时间为最后一个已完成的交易周期
        :param period: 周期
        :return:
        """
        d = datetime.datetime.strptime(date, dateFormat.value)
        if not dateutils.is_trading_day(f"{d.year}-{d.month}-{d.day}", DateFormat.Y_m_d):
            d = dateutils.getNextTradingDay(d.date())
            self._currentDateTime = datetime.datetime.now().replace(year=d.year, month=d.month, day=d.day, hour=0, minute=0, second=0)
        else:
            self._currentDateTime = d
        if period == Period.day:
            self._currentDateTime = self._currentDateTime.replace(hour=0, minute=0, second=0)
        elif period in [Period.minute30, Period.minute5, Period.minute1]:
            minutes = 30
            delta = datetime.timedelta(minutes=30)
            if period == Period.minute5:
                minutes = 5
                delta = datetime.timedelta(minutes=5)
            elif period == Period.minute1:
                minutes = 1
                delta = datetime.timedelta(minutes=1)
            hm = self._currentDateTime.hour * 100 + self._currentDateTime.minute
            if hm < 930:
                self._currentDateTime = self._currentDateTime.replace(hour=9, minute=30, second=0) + delta
            elif 930 <= hm < 1130 - minutes or 1300 <= hm < 1500 - minutes:
                if 930 < hm < 1130 - minutes:
                    self._currentDateTime = self._currentDateTime.replace(hour=9, minute=30, second=0)
                else:
                    self._currentDateTime = self._currentDateTime.replace(hour=13, minute=0, second=0)
                while True:
                    self._currentDateTime = self.getDateTime() + delta
                    hm2 = self._currentDateTime.hour * 100 + self._currentDateTime.minute
                    if hm2 >= hm:
                        break
            elif 1130 - minutes <= hm < 1300:
                self._currentDateTime = self._currentDateTime.replace(hour=11, minute=30, second=0)
            else:
                self._currentDateTime = self._currentDateTime.replace(hour=15, minute=0, second=0)
        r = R.sucess(f"重置时钟，当前时间：{self.getDateTime().strftime(DateFormat.Y_m_d_H_M_S.value)}",
                     self.getDateTime())
        self.event_manager.emit(Event(EventType.TIP_COMMON, r))
        logger.info(r)

    def flow(self, unit: Period = Period.minute30, skipNonTradingTime: bool = True):
        """
        时间流动
        :param unit: 单位
        :return:
        """
        dt = self.getDateTime()
        if unit == Period.day:
            d = dateutils.getNextTradingDay(dt.date())
            self._currentDateTime = self._currentDateTime.replace(year=d.year, month=d.month, day=d.day, hour=0, minute=0)
        elif unit == Period.minute30:
            hms = dt.hour * 100 + dt.minute
            if hms <= 930:
                self._currentDateTime = self._currentDateTime.replace(hour=10, minute=0)
            elif hms == 1130:
                self._currentDateTime = self._currentDateTime.replace(hour=13, minute=30)
            elif hms == 1500:
                d = dateutils.getNextTradingDay(dt.date())
                self._currentDateTime = self._currentDateTime.replace(year=d.year, month=d.month, day=d.day, hour=10, minute=0)
            else:
                self._currentDateTime = self._currentDateTime + datetime.timedelta(minutes=30)
        r = R.sucess(data=self.getDateTime(), msg=f"时钟更新，当前时间：{self.getDateTime().strftime(DateFormat.Y_m_d_H_M_S.value)}")
        self.event_manager.emit(Event(EventType.TIME_UPDATED, r))
        logger.info(str(r))

    def isEnd(self, unit: Period = Period.day) -> bool:
        date = dateutils.getLastestTradeday()
        dt2 = datetime.datetime.strptime(date.strftime(DateFormat.Y_m_d.value) + " 15:00:00",
                                         DateFormat.Y_m_d_H_M_S.value)
        if unit is not None and unit in [Period.minute30, Period.minute5, Period.minute1] and dt2 > datetime.datetime.now():
            dt2 = datetime.datetime.now()
        return self.getDateTime() > dt2

    def isInRealTradeTime(self) -> bool:
        dtNow = datetime.datetime.now()
        hm = dtNow.hour * 100 + dtNow.minute
        if hm < 930 or 1130 < hm < 1300 or hm > 1500:
            return False
        if not dateutils.is_trading_day(dtNow.strftime(DateFormat.Y_m_d.value), dateFormat=DateFormat.Y_m_d):
            return False
        dt = self.getDateTime()
        if dt < dtNow:
            return False
        return True

    def isInHistoryTradeTime(self) -> bool:
        return not self.isInRealTradeTime()

    def getPeriodLastestTradingDatetime(self, period: Period == Period.day) -> datetime.datetime:
        dt = self.getDateTime()
        ltdt = dateutils.getLastestTradingDate(dt)
        now = datetime.datetime.now()
        if ltdt.month == now.month and ltdt.day == now.day:
            if period in [Period.minute30, Period.minute5, Period.minute1]:
                if period == Period.minute30:
                    delta = datetime.timedelta(minutes=30)
                elif period == Period.minute5:
                    delta = datetime.timedelta(minutes=5)
                elif period == Period.minute1:
                    delta = datetime.timedelta(minutes=1)
                hm = now.hour * 100 + now.minute
                if hm < 930:
                    dt1 = now - datetime.timedelta(days=1)
                    return dt1.replace(hour=15, minute=0, second=0)
                elif 1130 > hm >= 930 or 1300 <= hm < 1500:
                    start = now.replace(hour=9, minute=30, second=0)
                    hm1 = start.hour * 100 + start.minute
                    while hm1 < hm:
                        start = start + delta
                        hm1 = start.hour * 100 + start.minute
                    return start
                elif hm == 1130:
                    return now
                elif hm >= 1500:
                    return now.replace(hour=15, minute=0, second=0)
            elif period == Period.day:
                hm = now.hour * 100 + now.minute
                if hm < 930:
                    dt1 = now - datetime.timedelta(days=1)
                    dt1 = dateutils.getLastestTradingDate(dt1)
                    return dt1.replace(hour=0, minute=0, second=0)
                else:
                    return now.replace(hour=0, minute=0, second=0)
        else:
            if period in [Period.minute30, Period.minute5, Period.minute1]:
                return ltdt.replace(hour=15, minute=0, second=0)
            elif period == Period.day:
                return ltdt.replace(hour=0, minute=0, second=0)
